/*This library relies on jQuery's each, inArray and extend api */
(function () {
	
    var DEFAULT_KEY_PROPERTY = 'id';
	
	//SOME UTILITIES
	if(Array.prototype.remove === undefined || Array.prototype.remove === null) {
		Array.prototype.remove = Array.prototype.remove || function (from, to) {
			var rest = this.slice((to || from) + 1 || this.length);
			this.length = from < 0 ? this.length + from : from;
			return this.push.apply(this, rest);
		};
	}

    function removeElementByIndex(array, index) {
        array.remove(index);
    }

	//CONSTRUCTOR
    function ObjectStore(keyProperty) {
        this._array = [];
        this._hash = {};
        this._keys = [];
        this._keyProperty = keyProperty ||
			DEFAULT_KEY_PROPERTY;
    }

	//ADDERS
    ObjectStore.prototype.add = function (obj) {
        if (!obj ||
			!obj[this._keyProperty]) {
            return;
        }

        var key = obj[this._keyProperty];

        if (this.hasKey(key)) {
            this.update(obj);
            return;
        }

        this._array.push(obj);
        this._hash[key] = obj;
        this._keys.push(key);
		
		return this;
    };

    ObjectStore.prototype.addCollection = function (objCollection) {
        var self = this;

        jQuery.each(
			objCollection,
			function (index, obj) {
			    self.add(obj);
			}
		);
		
		return self;
    };

    ObjectStore.prototype.addObjectStore = function (objectStore) {
        var self = this;

        objectStore.each(
			function (index, obj) {
			    self.add(obj);
			}
		);
		
		return self;
    };

	//UPDATER
    ObjectStore.prototype.update = function (obj) {
        if (!obj ||
			!obj[this._keyProperty]) {
            return;
        }

        var key = obj[this._keyProperty],
			  index = jQuery.inArray(key, this._keys);
        if (index === -1) {
            return;
        }

        this._array[index] = obj;
        this._hash[key] = obj;
        this._keys[index] = key;
		
		return this;
    };

	//REMOVERS
    ObjectStore.prototype.remove = function (obj) {
        if (!obj ||
			!obj[this._keyProperty]) {
            return;
        }

        var index = jQuery.inArray(obj, this._array);
        if (index === -1) {
            return;
        }

        removeElementByIndex(this._array, index);
        removeElementByIndex(this._keys, index);

        delete this._hash[obj[this._keyProperty]];
		
		return this;
    };

    ObjectStore.prototype.removeByKey = function (key) {
        this.remove(this._hash[key]);
		
		return this;
    };
	
	ObjectStore.prototype.clear = function () {
        this._array = [];
        this._hash = {};
        this._keys = [];
		
		return this;
    };

	//CHECKERS
    ObjectStore.prototype.hasValue = function (value) {
        if (!value ||
			!value[this._keyProperty]) {
            return false;
        }

        var index = jQuery.inArray(value, this._array);
        return index !== -1;
    };

    ObjectStore.prototype.hasKey = function (key) {
        if (this._hash[key]) {
            return true;
        }

        return false;
    };
	
	ObjectStore.prototype.isEmpty = function () {
        return this.getCount() === 0;
    };

	//ITERATOR
    ObjectStore.prototype.each = function (callback) {
        jQuery.each(this._array, callback);
		
		return this;
    };

	//GETTERS AND SETTERS
    ObjectStore.prototype.getValueByKey = function (key) {
        if (!key) {
            return null;
        }

        return this._hash[key];
    };

    ObjectStore.prototype.getValueByIndex = function (index) {
        if (index < 0 || index >= this._array.length) {
            return null;
        }

        return this._array[index];
    };

    ObjectStore.prototype.getCount = function () {
        return this._array.length;
    };

    ObjectStore.prototype.getKeys = function () {
        return this._keys;
    };

    ObjectStore.prototype.getArray = function () {
        return this._array;
    };

    ObjectStore.prototype.getHash = function () {
        return this._hash;
    };

    ObjectStore.prototype.getKeyProperty = function () {
        return this._keyProperty;
    };

    ObjectStore.prototype.setKeyProperty = function (keyProperty) {
        this._keyProperty = keyProperty;
    };

	//CLONER
	ObjectStore.prototype.clone = function () {
        return jQuery.extend(true, {}, this);
    };
	
	//FINDERS
    ObjectStore.prototype.find = function (finder) {
        var clonedObjectStore = this.clone();
        this.each(
			function (index, object) {
			    if (finder(index, object) === false) {
			        clonedObjectStore.remove(object);
			    }
			}
		);

        return clonedObjectStore;
    };

    ObjectStore.prototype.filter = function (filter) {
        var clonedObjectStore = this.clone();
        this.each(
			function (index, object) {
			    if (filter(index, object) === true) {
			        clonedObjectStore.remove(object);
			    }
			}
		);

        return clonedObjectStore;
    };

	//SORTING
    ObjectStore.prototype.sort = function (comparer) {
        this._array.sort(comparer);
        var self = this;
        this._keys.sort(
			function (a, b) {
			    return comparer(
					self.getValueByKey(a),
					self.getValueByKey(b)
				);
			}
		);
		
		return this;
    };

    ObjectStore.prototype.sortByProperty = function (sortProperty) {
        function comparer(a, b) {
            a = a[sortProperty];
            b = b[sortProperty];
            if (a > b) {
                return 1;
            }
            else if (a == b) {
                return 0;
            }
            else {
                return -1;
            }
        }

        this.sort(comparer);
		
		return this;
    };

    ObjectStore.prototype.sortByKey = function () {
        function comparer(a, b) {
            a = a[this._keyProperty];
            b = b[this._keyProperty];
            if (a > b) {
                return 1;
            }
            else if (a == b) {
                return 0;
            }
            else {
                return -1;
            }
        }

        this.sort(comparer);
		
		return this;
    };

    window.ObjectStore = ObjectStore;
} ());